<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    html,body{margin: 0;padding:0;}
  </style>
</head>
<body>
  <canvas class="cvs"></canvas>
</body>
<script>
  const cvs = document.querySelector(".cvs")
  cvs.width = window.innerWidth;
  cvs.height = window.innerHeight;
  
  const c2d = cvs.getContext("2d");

  const r = 3;

  const maxLen = 300;
  const minLen = 100;
  // 500 -- 0
  // 300 -- 0.25
  // 300 -- 0.5
  // 200 -- 0.75
  // 100 -- 1
  // (maxLen - 当前) / (maxLen - minLen)

  

  const points = [];
  for(let i=0;i<random(20,30);i++){
    points.push({
      x:random(0, cvs.width - r*2),
      y:random(0, cvs.height - r*2),
      speedX: random(-3, 3),
      speedY: random(-3, 3)
    })
  }

  let n=0;
  function fn(){
    // 绘制之前先擦除
    c2d.clearRect(0,0,cvs.width,cvs.height);
    // 并重新开启新路径
    c2d.beginPath();
    // 遍历所有点
    points.forEach(val1=>{
      // 绘制点
      c2d.beginPath();
      c2d.arc(val1.x, val1.y, r, 0, 2*Math.PI);
      c2d.fill();
      // 点运动：根据自身步长改变位置
      val1.x += val1.speedX;
      val1.y += val1.speedY;
      // 边界限定：反弹
      if(val1.x > cvs.width-2*r || val1.x < 0){
        val1.speedX = -val1.speedX;
      }
      if(val1.y > cvs.height-2*r || val1.y < 0){
        val1.speedY = -val1.speedY;
      }
      // 再次遍历所有点：为了连线
      points.forEach(val2=>{
        // 计算两个点之间的距离：c
        const a = val2.x-val1.x;
        const b = val2.y-val1.y;
        const c = Math.sqrt( a*a + b*b );
        // 划线：每两个点之间进行连线
        c2d.beginPath();
        c2d.moveTo(val1.x, val1.y);
        c2d.lineTo(val2.x, val2.y);
        // 根据两点之间的距离，计算线的透明度
        c2d.strokeStyle = `rgba(0,0,0,${(maxLen - c) / (maxLen - minLen)})`;
        c2d.stroke();
      })
    })
    // 动画
    requestAnimationFrame( fn );
  }
  requestAnimationFrame( fn );


  function random(max, min){
    return Math.round(Math.random()*(max-min)+min);
  }
  
</script>
</html>